import { ctx2 } from '@app-base/module';
import {
  Matrix4,
  OrthographicCamera,
  Plane,
  Quaternion,
  Raycaster,
  Vector2,
  Vector3,
  Vector3Tuple,
} from 'three-legacy';

// 就近（旋转角度尽量小）旋转到正对指定平面的对应法向和up
export const getNearFaceNormalAndUp = (frame: Vector3Tuple[]) => {
  const currentNormal = ctx2.viewer.sceneSetup.getCameraNormal().negate();
  const camera = ctx2.viewer.sceneSetup.camera as OrthographicCamera;

  const xDir = new Vector3(...frame[2]);
  const zDir = new Vector3(...frame[1]);
  const yDir = new Vector3().crossVectors(zDir, xDir);

  // 实际旋转时，旋转到平面在当前视角内能看到的一侧，up则就近旋转到x/y轴或其反向
  const needZReverse = currentNormal.clone().dot(zDir) < 0;

  // 求点乘，点乘绝对值大的一方夹角小
  const xDot = camera.up.clone().dot(xDir);
  const yDot = camera.up.clone().dot(yDir);

  const upRotateDot = Math.abs(xDot) > Math.abs(yDot) ? xDot : yDot;
  const upRotateTarget = Math.abs(xDot) > Math.abs(yDot) ? xDir : yDir;

  return {
    normal: needZReverse ? zDir.negate() : zDir,
    up: upRotateDot > 0 ? upRotateTarget : upRotateTarget.clone().negate(),
  };
};

export function calculateDeltaMatrix(
  pointerDownPos: { x: number; y: number },
  pointerCurrentPos: { x: number; y: number },
  originalMatrix: Matrix4,
): Matrix4 {
  const camera = ctx2.viewer.sceneSetup.camera as OrthographicCamera;

  const raycaster = new Raycaster();
  const cameraDirection = new Vector3();
  camera.getWorldDirection(cameraDirection);

  const position = new Vector3();
  const quaternion = new Quaternion();
  const scale = new Vector3();

  originalMatrix.decompose(position, quaternion, scale);

  const planePoint = position.clone();
  const plane = new Plane().setFromNormalAndCoplanarPoint(
    cameraDirection,
    planePoint,
  );
  const pointerToWorld = (pointer: { x: number; y: number }) => {
    const normalizedPointer = new Vector2(
      (pointer.x / window.innerWidth) * 2 - 1,
      -(pointer.y / window.innerHeight) * 2 + 1,
    );
    raycaster.setFromCamera(normalizedPointer, camera);
    const intersection = new Vector3();
    raycaster.ray.intersectPlane(plane, intersection);
    return intersection;
  };

  const downWorldPos = pointerToWorld(pointerDownPos);
  const currentWorldPos = pointerToWorld(pointerCurrentPos);

  const delta = new Vector3().subVectors(currentWorldPos, downWorldPos);
  const deltaMatrix = new Matrix4().makeTranslation(delta.x, delta.y, delta.z);

  return deltaMatrix;
}
